package com.annet.token.aop;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.annet.token.annotation.SecurityParameter;
import com.annet.token.dto.ResponseDTO;
import com.annet.token.utils.AESUtils;
import com.annet.token.utils.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 安全字段加密解密切面
 *
 * @author: chenkaidong
 * @date:2021/1/18
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
@Aspect
@Component
@Slf4j
public class SecurityParamAop {

    @Value("${annet.rsa.privateKey}")
    private String rasPrivateKey;


    private String encode = "UTF-8";


    @Pointcut("@annotation(com.annet.token.annotation.SecurityParameter)")
    public void annotationPointCut() {
    }


    @Around("annotationPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        Object responseObj = null;

        // 获取目标方法上的注解
        Class<?> classTarget = joinPoint.getTarget().getClass();
        String methodName = joinPoint.getSignature().getName();
        Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Method objMethod = classTarget.getMethod(methodName, par);
        SecurityParameter securityParameter = objMethod.getAnnotation(SecurityParameter.class);


        try {
            Object[] objects = joinPoint.getArgs();
            if (securityParameter.inDecode()) {
                // 入参解密
                objects = handleEncrypt(joinPoint);
            }
            responseObj = joinPoint.proceed(objects);
            // 出参加密
            if (securityParameter.outEncode()) {
                handleDecrypt(responseObj);
            }

        } catch (Throwable throwable) {
            throwable.printStackTrace();
            log.error("SecurityParameterAop处理出现异常{}", throwable);
        }
        return responseObj;
    }

    /**
     * 处理加密入参
     *
     * @param joinPoint
     */
    private Object[] handleEncrypt(ProceedingJoinPoint joinPoint) throws Exception {

        // 排除多参数注入问题
        JSONObject requestObj = null;
        boolean flag = false;
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            JSONObject jsonObject = (JSONObject) arg;
            if (StrUtil.isNotBlank(jsonObject.getString("encryptData"))) {
                requestObj = jsonObject;
                flag = true;
            }
        }
        if (!flag) {
            throw new Exception("入参数据没有encryptData属性或encryptData数据为空");
        }

        String aseKey = getAseKey();

        String jsonData = AESUtils.decrypt(aseKey, requestObj.getString("encryptData"), encode);
        JSONObject jsonObject = JSON.parseObject(jsonData);

        // 重新构造Object[]
        Object[] result = new Object[1];
        result[0] = jsonObject;

        return result;
    }

    /**
     * 处理解密出参
     *
     * @param responseObj
     */
    private Object handleDecrypt(Object responseObj) throws Exception {
        if (ObjectUtil.isNull(responseObj)) {
            return null;
        }

        ResponseDTO responseDTO = (ResponseDTO) responseObj;
        String data = (String) responseDTO.getData();
        if (StrUtil.isNotBlank(data)) {
            String aseKey = getAseKey();
            String encrypt = AESUtils.encrypt(aseKey, data, encode);
            responseDTO.setData(encrypt);
        }
        return responseDTO;
    }


    /**
     * 获取请求投上的客户端密钥
     *
     * @return
     */
    private String getAseKey() {
        // 获取密钥
        HttpServletRequest request = getRequest();
        String encryptKey = request.getHeader("encryptKey");
        String aseKey = "";
        try {
            aseKey = RSAUtils.decrypt(encryptKey, rasPrivateKey);
            log.info("解密出来的ASE密钥为" + aseKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("客户端密钥解密失败");
        }
        return aseKey;
    }

    /**
     * 获取当前request
     *
     * @return
     */
    private HttpServletRequest getRequest() {
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        return (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
    }
}
